﻿using System;
using System.Linq;
using System.Runtime.InteropServices;

namespace Btstack
{
    public partial class Bluetooth
    {
        public enum ScanType
        {
            Passive,
            Active
        }

        public enum AdvType
        {
            AdvInd,
            AdvDirectInd,
            AdvNonconnInd,
            ScanReq,
            ScanResp,
            ConnectReq,
            AdvScanInd
        }

        public enum GapRandomAddressType
        {
            TypeOff,
            TypeStatic,
            NonResolvable,
            Resolvable
        }

        private enum GapSecurityLevel
        {
            Level0,
            Level1,
            Level2,
            Level3,
            Level4
        }

        [DllImport("btstack-c.dll", EntryPoint = "gap_local_bd_addr")]
        private static extern void GapLocalBdAddr(IntPtr AddressBuffer);

        [DllImport("btstack-c.dll", EntryPoint = "gap_set_connection_parameters")]
        private static extern void GapSetConnectionParameters(ushort ConnScanInterval, ushort ConnScanWindow,
            ushort ConnIntervalMin, ushort conn_interval_max, ushort ConnLatency,
            ushort CupervisionTimeout, ushort MinCeLength, ushort MaxCeLength);

        [DllImport("btstack-c.dll", EntryPoint = "gap_connect")]
        private static extern byte GapConnect(IntPtr addr, BdAddrType type);

        [DllImport("btstack-c.dll", EntryPoint = "gap_set_scan_parameters")]
        private static extern void GapSetScanParameters(ScanType type, ushort interval, ushort window);

        [DllImport("btstack-c.dll", EntryPoint = "gap_start_scan")]
        private static extern void GapStartScan();

        [DllImport("btstack-c.dll", EntryPoint = "gap_stop_scan")]
        private static extern void GapStopScan();

        [DllImport("btstack-c.dll", EntryPoint = "gap_disconnect")]
        private static extern byte GapDisconnect(ushort handle);

        [DllImport("btstack-c.dll", EntryPoint = "gap_advertisements_set_params")]
        private static extern void GapAdvertisementsSetParams(ushort AdvIntMin, ushort AdvIntMax, AdvType AdvType,
            BdAddrType DirectAddressType, IntPtr DirectAddress, byte ChannelMap, byte FilterPolicy);

        [DllImport("btstack-c.dll", EntryPoint = "gap_advertisements_set_data")]
        private static extern void GapAdvertisementsSetData(byte length, IntPtr data);

        [DllImport("btstack-c.dll", EntryPoint = "gap_advertisements_enable")]
        private static extern void GapAdvertisementsEnable(bool enabled);

        [DllImport("btstack-c.dll", EntryPoint = "gap_request_connection_parameter_update")]
        private static extern int GapRequestConnectionParameterUpdate(ushort handle, ushort IntervalMin, 
            ushort IntervalMax, ushort latency, ushort SupervisionTimeout);

        [DllImport("btstack-c.dll", EntryPoint = "gap_read_rssi")]
        private static extern int GapReadRssi(ushort handle);

        [DllImport("btstack-c.dll", EntryPoint = "gap_random_address_get_mode")]
        private static extern GapRandomAddressType GapRandomAddressGetMode();

        [DllImport("btstack-c.dll", EntryPoint = "gap_random_address_set_mode")]
        private static extern void GapRandomAddressSetMode(GapRandomAddressType type);

        [DllImport("btstack-c.dll", EntryPoint = "gap_random_address_set_update_period")]
        private static extern void GapRandomAddressSetUpdatePeriod(int period);

        private static byte[] GapLocalBdAddr()
        {
            IntPtr pnt = Marshal.AllocHGlobal(6);

            GapLocalBdAddr(pnt);

            var addr = new byte[6];

            Marshal.Copy(pnt, addr, 0, 6);

            Marshal.FreeHGlobal(pnt);

            return addr;
        }

        private static BdAddrType GapEeventAdvertisingReportGetAddressType(byte[] packet)
        {
            return (BdAddrType)packet[3];
        }

        private static byte[] GapEventAdvertisingReportGetAddress(byte[] packet)
        {
            var addr = packet.Skip(4).Take(6).ToArray();
            Array.Reverse(addr);
            return addr;
        }

        private static sbyte GapEventAdvertisingReportGetRssi(byte[] packet)
        {
            return (sbyte)packet[10];
        }

        private static string GapAdvertisementReportName(byte[] packet)
        {
            var AdvData = packet.Skip(12).Take(packet[11]).ToArray();

            while (AdvData.Length > 0)
            {
                byte data_type = AdvData[1];
                int size = AdvData[0] - 1;
                var data = AdvData.Skip(2).Take(size).ToArray();

                switch (data_type)
                {
                    case BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME:
                    case BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME:
                        return System.Text.Encoding.UTF8.GetString(data);
                }

                AdvData = AdvData.Skip(2 + size).ToArray();
            }

            return "";
        }

        private static void GapAdvertisementsSetParams(ushort AdvIntMin, ushort AdvIntMax, AdvType AdvType,
            BdAddrType DirectAddressType, byte[] DirectAddress, byte ChannelMap, byte FilterPolicy)
        {
            var DirectAddressPtr = Marshal.AllocHGlobal(6);
            Marshal.Copy(DirectAddress, 0, DirectAddressPtr, 6);
            GapAdvertisementsSetParams(AdvIntMin, AdvIntMax, AdvType, DirectAddressType, DirectAddressPtr, ChannelMap, FilterPolicy);
            Marshal.FreeHGlobal(DirectAddressPtr);
        }

        private static void GapAdvertisementsSetData(byte[] data)
        {
            var DataPtr = Marshal.AllocHGlobal(data.Length);
            Marshal.Copy(data, 0, DataPtr, data.Length);

            GapAdvertisementsSetData((byte)data.Length, DataPtr);
        }

        private static ushort GapEventRssiMeasurementGetConHandle(byte[] packet)
        {
            return BitConverter.ToUInt16(packet, 2);
        }

        private static sbyte GapEventRssiMeasurementGetRssi(byte[] packet)
        {
            return (sbyte)packet[4];
        }

        private static AdvType GapEventAdvertisingReportGetAdvertisingEventType(byte[] packet)
        {
            return (AdvType)packet[2];
        }

        private static void GapConnect(string addr, BdAddrType type)
        {
            addr = addr.Replace("-", "").Replace(":", "").Replace(" ", "");

            if (addr.Length != 12)
                throw new ArgumentException("Invalid address");

            var AddrBytes = new byte[6];

            for (int i = 0; i < AddrBytes.Length; i++)
            {
                AddrBytes[i] = Convert.ToByte(addr.Substring(i * 2, 2), 16);
            }

            IntPtr AddrPtr = Marshal.AllocHGlobal(6);
            Marshal.Copy(AddrBytes, 0, AddrPtr, 6);
            GapConnect(AddrPtr, type);
            Marshal.FreeHGlobal(AddrPtr);
        }

        private static void GapConnect(byte[] addr, BdAddrType type)
        {
            IntPtr AddrPtr = Marshal.AllocHGlobal(6);
            Marshal.Copy(addr, 0, AddrPtr, 6);
            GapConnect(AddrPtr, type);
            Marshal.FreeHGlobal(AddrPtr);
        }
    }
}
